package org.theqcp.wow.sim.engine.old;

import java.util.Map;
import java.util.StringTokenizer;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.theqcp.wow.model.exceptions.PlayerSetupException;
import org.theqcp.wow.model.items.WeaponType;
import org.theqcp.wow.model.mechanics.Attribute;
import org.theqcp.wow.model.mechanics.ClassType;
import org.theqcp.wow.model.mechanics.Race;
import org.theqcp.wow.model.mechanics.Slot;
import org.theqcp.wow.model.player.Player;
import org.theqcp.wow.model.player.PlayerFactory;
import org.theqcp.wow.sim.engine.revised.EquipmentPointConfig;
import org.theqcp.wow.sim.engine.revised.EquipmentPointConfig.Method;
import org.theqcp.wow.sim.engine.revised.scripts.AutoAttackObserver;
import org.theqcp.wow.sim.engine.revised.scripts.Enchantment;
import org.theqcp.wow.sim.engine.revised.scripts.EnchantmentFactory;
import org.theqcp.wow.sim.engine.revised.scripts.Imbue;
import org.theqcp.wow.sim.engine.revised.scripts.ImbueFactory;

public class Configurator {

    private static Configurator me = null;

    public static EnhSimConfig createNewConfig( ClassType classType, Map<String, String> config )
	    throws PlayerSetupException {
	if( me == null ) {
	    me = new Configurator();
	}
	Player player = PlayerFactory.createPlayer(classType);
	return me.createPlayerConfig(player, config);
    }

    protected final Logger log = LogManager.getLogger(getClass().getSimpleName());

    public EnhSimConfig createPlayerConfig( Player player, Map<String, String> config ) throws PlayerSetupException {
	EnhSimConfig enhConfig = new EnhSimConfig(player);

	{ // Configure simulation
	    enhConfig.setThreads(atoi(config.get("threads")));
	}

	{ // Configure base equipment point calculation data
	    EquipmentPointConfig epc = new EquipmentPointConfig();

	    if( config.get("ep_base_stat").equalsIgnoreCase("ap") ) {
		epc.setMethodBase(Method.AttackPower);
	    } else {
		epc.setMethodBase(Method.SpellPower);
	    }

	    epc.setPrecision(atod(config.get("ep_precision")));
	    epc.setAttackPower(atod(config.get("ep_ap")));
	    epc.setCritRating(atod(config.get("ep_crit_rating")));
	    epc.setHitRating(atod(config.get("ep_hit_rating")));
	    epc.setExpertise(atod(config.get("ep_expertise")));
	    epc.setHasteRating(atod(config.get("ep_haste_rating")));
	    epc.setArmorPenetrationRating(atod(config.get("ep_armor_penetration_rating")));
	    epc.setSpellPower(atod(config.get("ep_spellpower")));
	    epc.setDps(atod(config.get("ep_dps")));
	    epc.setMana(atod(config.get("ep_mana")));
	    epc.setSpirit(atod(config.get("ep_spirit")));
	    epc.setMp5(atod(config.get("ep_mp5")));

	    enhConfig.setEquipmentPointConfig(epc);
	}

	{// Configure player
	    player.setRace(Race.valueOf(config.get("race").toUpperCase()));

	    player.setBaseStat(Attribute.STRENGTH, atod(config.get("str")));
	    player.setBaseStat(Attribute.AGILITY, atod(config.get("agi")));
	    player.setBaseStat(Attribute.INTELLECT, atod(config.get("int")));
	    player.setBaseStat(Attribute.SPIRIT, atod(config.get("spi")));

	    player.setLevel(80);

	    player.setBaseStat(Attribute.CRITPERCENT, (atod(config.get("mh_crit")) + atod(config.get("oh_crit"))) / 2);
	    player.setBaseStat(Attribute.EXPERTISERATING,
		    (atod(config.get("mh_expertise_rating")) + atod(config.get("oh_expertise_rating"))) / 2);
	}

	{ // Configure player's metadata
	    // TODO: refactor as much of this as possible into Player itself via
	    // item downloads
	    enhConfig.addObserver(getImbue(Slot.MAINHAND, config.get("mh_imbue")));
	    enhConfig.addObserver(getEnchantment(Slot.MAINHAND, config.get("mh_enchant")));
	    enhConfig.addObserver(new AutoAttackObserver(Slot.MAINHAND, atod(config.get("mh_dps")),
		    atod(config.get("mh_speed"))));
	    enhConfig.setWeaponType(Slot.MAINHAND, (atoi(config.get("mh_weapon")) == 1) ? (WeaponType.AXE)
		    : (WeaponType.UNKNOWN));

	    enhConfig.addObserver(getImbue(Slot.OFFHAND, config.get("oh_imbue")));
	    enhConfig.addObserver(getEnchantment(Slot.OFFHAND, config.get("oh_enchant")));
	    enhConfig.addObserver(new AutoAttackObserver(Slot.OFFHAND, atod(config.get("oh_dps")),
		    atod(config.get("oh_speed"))));
	    enhConfig.setWeaponType(Slot.OFFHAND, (atoi(config.get("oh_weapon")) == 1) ? (WeaponType.AXE)
		    : (WeaponType.AXE));

	}

	return enhConfig;
    }

    private Imbue getImbue( Slot slot, String imbueType ) {
	Imbue imbue = null;

	try {
	    imbue = ImbueFactory.newObserver(imbueType);
	    imbue.fireForSlot(slot);
	} catch( ClassNotFoundException e ) {
	    log.error(e);
	    log.debug(e, e);
	}

	return imbue;
    }

    private Enchantment getEnchantment( Slot slot, String enchantType ) {
	Enchantment enchant = null;

	try {
	    enchant = EnchantmentFactory.newObserver(enchantType);
	    enchant.fireForSlot(slot);
	} catch( ClassNotFoundException e ) {
	    log.error(e);
	    log.debug(e, e);
	}

	return enchant;
    }

    private int getTalentRank( String value ) {
	return Integer.valueOf(getBoundedValue(value));
    }

    private float getBuffValue( String value ) {
	return Float.valueOf(getBoundedValue(value));
    }

    private String getBoundedValue( String value ) {
	StringTokenizer st = new StringTokenizer(value, "/");

	if( st.countTokens() > 0 ) {
	    value = st.nextToken();
	}

	return value;
    }

    /**
     * Convenience function for converting a string to an int
     * 
     * @param value
     * @return
     */
    private int atoi( String value ) {
	if( value == null ) {
	    return 0;
	} else if( value.equals("-") ) {
	    return 0;
	} else {
	    return Integer.valueOf(value);
	}
    }

    /**
     * Convenience function for converting a string to a float
     * 
     * @param value
     * @return
     */
    private double atod( String value ) {
	if( value == null ) {
	    return 0;
	} else if( value.equals("-") ) {
	    return 0;
	} else {
	    return Double.valueOf(value);
	}
    }

}
